<!DOCTYPE html>
<html>
<head>
<script src="../../../resources/js-test.js"></script>
<script src="../../../../../blink/perf_tests/resources/runner.js"></script>
<script type="text/javascript">
var alternateComputeStatistics = {
    min: function(array) {
        return Math.min.apply(Math, array);
    },
    max: function(array) {
        return Math.max.apply(Math, array);
    },
    median: function(originalData) {
        // don't want side effects on the input array, so...
        var array = originalData.slice(0);
        array.sort(function(a,b) { return a - b; });
        var mid = Math.floor(array.length / 2);
        if (array.length % 2)
            return array[mid];
        else
            return (array[mid-1] + array[mid]) / 2;
    },
    mean: function(array) {
        return alternateComputeStatistics.sum(array)/array.length;
    },
    sum: function(array) {
        var total = 0;
        for (var index in array)
            total += array[index];
        return total;
    },
    variance: function(array) {
        var mean = alternateComputeStatistics.mean(array);
        var sumOfSquaredDiffs = 0;
        for (var index in array) {
            var squaredDiff = array[index] - mean;
            sumOfSquaredDiffs += squaredDiff * squaredDiff;
        };
        return sumOfSquaredDiffs / (array.length - 1);
    },
    stdev: function(array) {
        return Math.sqrt(alternateComputeStatistics.variance(array));
    }
};
</script>
</head>
<body>

<div id="description"></div>

<script type="text/javascript">
description("This test verifies PerfTestRunner.computeStatistics(), " +
            "including: min, max, median, mean, variance, and stdev.");
</script>

<div id="console"></div>

<script type="text/javascript">
var data = [];
var stats = [];

data = [0, 0, 0, 0, 0];
debug("Ensure no latent divide by 0's for an odd number of elements.");
debug("data = " + JSON.stringify(data));
stats = PerfTestRunner.computeStatistics(data);
shouldEvaluateTo("stats.min", 0);
shouldEvaluateTo("stats.max", 0);
shouldEvaluateTo("stats.median", 0);
shouldEvaluateTo("stats.mean", 0);
shouldEvaluateTo("stats.variance", 0);
shouldEvaluateTo("stats.stdev", 0);
debug("");

data = [1, 10, 2, 20, 5];
debug("This test will catch if any order dependencies in the data, such as");
debug("needing to be numerically sorted, are not resolved by the algorithm.");
debug("This variant covers an odd number of elements.");
debug("data = " + JSON.stringify(data));
stats = PerfTestRunner.computeStatistics(data);
// hand calculated
shouldEvaluateTo("stats.min", 1);
shouldEvaluateTo("stats.max", 20);
shouldEvaluateTo("stats.median", 5);
shouldEvaluateTo("stats.mean", (38/5));
// using alternate implementation
shouldEvaluateTo("stats.min", alternateComputeStatistics.min(data));
shouldEvaluateTo("stats.max", alternateComputeStatistics.max(data));
shouldEvaluateTo("stats.median", alternateComputeStatistics.median(data));
shouldEvaluateTo("stats.mean", alternateComputeStatistics.mean(data));
shouldBeCloseTo("stats.variance", alternateComputeStatistics.variance(data), 0.0001);
shouldBeCloseTo("stats.stdev", alternateComputeStatistics.stdev(data), 0.0001);
debug("");

data = [-1, -10, -2, -20, -5];
debug("This test will catch if any order dependencies in the data, such as");
debug("needing to be numerically sorted, are not resolved by the algorithm.");
debug("This variant covers an odd number of elements, and negative values.");
debug("data = " + JSON.stringify(data));
stats = PerfTestRunner.computeStatistics(data);
// hand calculated
shouldEvaluateTo("stats.min", -20);
shouldEvaluateTo("stats.max", -1);
shouldEvaluateTo("stats.median", -5);
shouldEvaluateTo("stats.mean", (-38/5));
// using alternate implementation
shouldEvaluateTo("stats.min", alternateComputeStatistics.min(data));
shouldEvaluateTo("stats.max", alternateComputeStatistics.max(data));
shouldEvaluateTo("stats.median", alternateComputeStatistics.median(data));
shouldEvaluateTo("stats.mean", alternateComputeStatistics.mean(data));
shouldBeCloseTo("stats.variance", alternateComputeStatistics.variance(data), 0.0001);
shouldBeCloseTo("stats.stdev", alternateComputeStatistics.stdev(data), 0.0001);
debug("");

data = [0, 0, 0, 0];
debug("Ensure no latent divide by 0's for an even number of elements.");
debug("data = " + JSON.stringify(data));
stats = PerfTestRunner.computeStatistics(data);
shouldEvaluateTo("stats.median", 0);
debug("");

data = [1, 10, 2, 20, 5, 6];
debug("This test verifies that median is handled correctly for");
debug("an even number of elements.");
debug("data = " + JSON.stringify(data));
stats = PerfTestRunner.computeStatistics(data);
shouldEvaluateTo("stats.median", 5.5);
shouldEvaluateTo("stats.median", alternateComputeStatistics.median(data));
debug("");

data = [-1, -10, -2, -20, -5, -6];
debug("This test verifies that median is handled correctly for");
debug("an even number of elements, including negative numbers.");
debug("data = " + JSON.stringify(data));
stats = PerfTestRunner.computeStatistics(data);
shouldEvaluateTo("stats.median", -5.5);
shouldEvaluateTo("stats.median", alternateComputeStatistics.median(data));
debug("");

// runner.js marks this as an async test so we need to call notifyDone.
if (window.testRunner)
    testRunner.notifyDone();
</script>

</body>
</html>
